SQL과 같은 계층적 언어를 해석하기 위해 계층 구조를 표현할 수 있습니다.
여러 객체를 조합해 문법을 정의합니다. 각각의 객체는 특정한 문법을 처리할 수 있도록 구현됩니다.
이들이 모여 컴포지트 패턴과 같은 복합적인 트리 구조로 이루어지게 됩니다. 이는 하위 객체가 처리한 결과를 조합하여 새로운 결과를 만들어내게 해줍니다.
사용자가 원하는 다양한 명령을 쉽게 표현할 수 있게 구문 약속을 해야하며, 해석자에서는 이와 같이 약속된 구문이 입력 인자로 전달되었을 때 이를 해석할 수 있어야 합니다.
위와 같은 경우에 사용한다면 문법의 수정이나 새로운 문법을 추가하기 용이해지지만 문법이 복잡해진다면 문법을 정의하는 객체 구조가 복잡해져 관리가 어려워집니다.
간단한 문법에 한해 사용하길 권장됩니다
다이어그램으로 보니 Composite 패턴과 유사합니다.
파일을 로드하고 삭제하는 명령어를 수행하는 프로그램을 인터프리터 패턴을 이용해 만들었습니다.
컴포지트 패턴을 이용해서 유연한 포함관계를 하려 하였으나 [명령어 - 파일리스트] 구조가 더 깔끔할 것 같아서 ActionExpression 내에 FileExpression만 포함되도록 하였습니다.
예시를 간단한 이미지로 보다면 다음과 같습니다.
interface AbstractExpression { | |
public void interprete(); | |
} | |
class FileExpression implements AbstractExpression { | |
String type; | |
ArrayList<String> fileList = new ArrayList<String>(); | |
public FileExpression(String type) { | |
// Type은 IMAGE, VIDEO, TEXT로 한정 | |
if (type.equals("IMAGE")) { | |
this.type = "IMAGE"; | |
} else if (type.equals("VIDEO")) { | |
this.type = "VIDEO"; | |
} else if (type.equals("TEXT")) { | |
this.type = "TEXT"; | |
} else { | |
System.out.println("type error"); | |
} | |
} | |
public void addFile(String fileName) { | |
fileList.add(fileName); | |
} | |
@Override | |
public void interprete() { | |
String str = type + ": "; | |
for (String file : fileList) { | |
str += file + " "; | |
} | |
System.out.println(str); | |
} | |
} | |
class ActionExpression implements AbstractExpression { | |
private String actionName; | |
private HashMap<String, FileExpression> expressionList = new HashMap<String, FileExpression>(); | |
public ActionExpression(String actionName) { | |
this.actionName = actionName; | |
} | |
public FileExpression getFile(String fileType) { | |
// File을 return | |
// 해당 type의 File이 없는 경우 생성 후 return | |
FileExpression file = expressionList.get(fileType); | |
if (file == null) { | |
expressionList.put(fileType, new FileExpression(fileType)); | |
file = expressionList.get(fileType); | |
} | |
return file; | |
} | |
public HashMap<String, FileExpression> getExpressionList() { | |
return expressionList; | |
} | |
@Override | |
public void interprete() { | |
System.out.println("Action: " + actionName); | |
for (FileExpression expression : expressionList.values()) { | |
expression.interprete(); | |
} | |
// 해석 완료 후 초기화 | |
expressionList.clear(); | |
} | |
} |
class Interpreter { | |
String context; | |
HashMap<String, ActionExpression> actionExpressions = new HashMap<String, ActionExpression>(); | |
public Interpreter(String context) { | |
// 필요한 명령어 제한 및 생성 | |
this.context = context; | |
actionExpressions.put("LOAD", new ActionExpression("LOAD")); | |
actionExpressions.put("DELETE", new ActionExpression("DELETE")); | |
} | |
public void setContext(String context) { | |
this.context = context; | |
} | |
public void run() { | |
// Context Interpret | |
StringTokenizer stk = new StringTokenizer(context); | |
ActionExpression actionExpression = null; | |
while (stk.hasMoreTokens()) { | |
String tmpContext = stk.nextToken(); | |
AbstractExpression tmpExpression = actionExpressions.get(tmpContext); | |
if (tmpExpression != null) { | |
// Nonterminal인 경우: tmpContext는 LOAD, DELETE | |
// 한 Context 해석 중 Nonterminal 명령어가 두 번 이상 나올 경우, 이전 Nonterminal을 먼저 해석한 후 넘어간다. | |
if (actionExpression != null) { | |
actionExpression.interprete(); | |
} | |
actionExpression = (ActionExpression) tmpExpression; | |
} else { | |
// Terminal인 경우 - tmpContext: 파일 타입 | |
if (!stk.hasMoreTokens()) { | |
System.out.println("Input error: File Name"); | |
} | |
String fileName = stk.nextToken(); | |
FileExpression file = actionExpression.getFile(tmpContext); | |
file.addFile(fileName); | |
} | |
} | |
actionExpression.interprete(); | |
} | |
} |
public class Main { | |
public static void main(String[] args) { | |
System.out.println("Context_01------------------------------"); | |
String Context_01 = "LOAD IMAGE flower.jpg VIDEO happy.mp3"; | |
Interpreter interpreter = new Interpreter(Context_01); | |
interpreter.run(); | |
System.out.println("Context_02------------------------------"); | |
String Context_02 = "DELETE IMAGE delete.jpg VIDEO blue.mp3"; | |
interpreter.setContext(Context_02); | |
interpreter.run(); | |
System.out.println("Context_03------------------------------"); | |
String Context_03 = "LOAD IMAGE flower.jpg DELETE IMAGE delete.jpg VIDEO blue.mp3"; | |
interpreter.setContext(Context_03); | |
interpreter.run(); | |
} | |
} |
여러 형태로 Context를 생성했습니다.
세 번 째의 경우 Action 명령어가 두 번 포함되었습니다.
실행 결과는 다음과 같습니다.
Context_01------------------------------
Action: LOAD
IMAGE: flower.jpg
VIDEO: happy.mp3
Context_02------------------------------
Action: DELETE
IMAGE: delete.jpg
VIDEO: blue.mp3
Context_03------------------------------
Action: LOAD
IMAGE: flower.jpg
Action: DELETE
IMAGE: delete.jpg
VIDEO: blue.mp3
Action이 두 번 등장해도 오류 없이 잘 작동합니다.
간단한 문장 해석에도 굉장히 긴 코드가 필요하네요. 더 복잡해 질 경우엔 작성 시간과 수행 시간 모두 오래 걸릴 것 같습니다.
구현하기 너무 복잡한 경우, 컴파일러나 파서를 쓰는게 더 효율적일 수도 있다고 합니다.
참조
감사합니다.
Text by Chaelin. Photographs by Chaelin, Unsplash.